package com.gw.booking.common;


import com.gw.booking.dto.common.GPSCalculateDto;
import com.gw.booking.entity.LoginToken;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.commons.lang.time.DateUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.*;

public class Util {

    static Properties properties = null;
    static String encryptPwd = "SDFDSF234L23KSDUWLJFLSDFJ";
    static DecimalFormat format = new DecimalFormat("0.00");

    public static void assertAfterNow(Date expirationAt) {
        if (new Date().after(expirationAt)) {
            throw new ApiException(MessageCode.CODE_DATE_BEFORE_NOW);
        }
    }

    /**
     * 确认是本人操作
     */
    public static void assertOperator(Integer userId, Integer expectedUserId) {
        if (!userId.equals(expectedUserId)) {
            throw new ApiException(MessageCode.CODE_NO_PRIVILEGE);
        }
    }

    /**
     * 确认是本人操作
     */
    public static void assertOperator(String userType, Integer userId, String expectedUserType, Integer expectedUserId) {
        if (!StringUtils.equals(userType, expectedUserType) || !userId.equals(expectedUserId)) {
            throw new ApiException(MessageCode.CODE_NO_PRIVILEGE);
        }
    }

    /**
     * URI参数编码
     */
    public static String encodeUriParam(String param) {
        try {
            if (param == null) {
                return null;
            }

//			return new String(param.getBytes("ISO-8859-1"), "UTF-8");
            return new String(param.getBytes("UTF-8"), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return "";
        }
    }

    /**
     * 格式化数字，保留两位小数
     */
    public static String formatNumber(Number number) {
        if (number == null) {
            return "";
        }
        return format.format(number.doubleValue());
    }

    /**
     * 格式化数字，保留两位小数
     */
    public static String formatNumber(Double number) {
        return format.format(number);
    }

    public static <T> T getFromMap(Map map, Object key, Class<T> classT) {
        T result = (T) map.get(key);

        if (result == null) {
            try {
                result = classT.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            map.put(key, result);
        }

        return result;
    }

    public static void loadMessage() throws UnsupportedEncodingException, IOException {
        properties = new Properties();
        properties.load(new InputStreamReader(Util.class.getResourceAsStream("/message.property"), "UTF-8"));
    }

    public static BigDecimal nullToZero(BigDecimal otherPayAmount) {
        return otherPayAmount == null ? BigDecimal.ZERO : otherPayAmount;
    }

    public static String getMessage(String key, String... argv) {
        if (properties == null) {
            try {
                loadMessage();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        String message = properties.getProperty(key);

        if (argv != null) {
            for (String arg : argv) {
                message = message.replaceFirst("\\{[0-9]*\\}", arg);
            }
        }

        return message;
    }

    /**
     * 取得登录token
     */
    public static LoginToken getLoginToken(HttpServletRequest request) {
        LoginToken loginToken = (LoginToken) request.getAttribute("loginToken");

        return loginToken;
    }

    public static <T> List<T> convertBeanList(List<Object> list,
                                              Class<T> className) throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        List<T> resultList = new ArrayList<T>();

        if (list == null) {
            return resultList;
        }

        for (Object object : list) {
            T result = className.newInstance();
            PropertyUtils.copyProperties(result, object);

            resultList.add(result);
        }

        return resultList;
    }

    /**
     * 计算首条记录位置
     */
    public static int calFirst(int page, int size) {
        int first = (page - 1) * size;

        return first < 0 ? 0 : first;
    }

    /**
     * 验证登录密码
     */
    public static void checkPassword(String password, String passwordSigned) {
        if (!StringUtils.equalsIgnoreCase(DigestUtils.md5Hex(password), passwordSigned)) {
            throw new ApiException(MessageCode.CODE_LOGIN_ERROR); // 需要改成 throw new ApiException(MessageCode.CODE_PASSWORD_ERROR);
        }
    }

    /**
     * 加密
     *
     * @param datasource byte[]
     * @return byte[]
     */
    public static String encrypt(String datasource) {
        return encrypt(datasource, encryptPwd);
    }

    /**
     * 加密
     *
     * @param datasource byte[]
     * @param password   String
     * @return byte[]
     */
    public static String encrypt(String datasource, String password) {
        try {
            int passwordLength = password.length() + (8 - password.length() % 8);
            password = StringUtils.leftPad(password, passwordLength, '0');

            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(password.getBytes("UTF-8"));
            //创建一个密匙工厂，然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            //Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            //用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
            //现在，获取数据并加密
            //正式执行加密操作
            byte[] result = cipher.doFinal(datasource.getBytes("UTF-8"));

            return Base64.encodeBase64String(result);
        } catch (Throwable e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 解密
     *
     * @param src Base64字符串
     * @return byte[]
     * @throws Exception
     */
    public static String decrypt(String src) {
        return decrypt(src, encryptPwd);
    }

    /**
     * 解密
     *
     * @param src      Base64字符串
     * @param password String
     * @return byte[]
     * @throws Exception
     */
    public static String decrypt(String src, String password) {
        try {
            int passwordLength = password.length() + (8 - password.length() % 8);
            password = StringUtils.leftPad(password, passwordLength, '0');

            // DES算法要求有一个可信任的随机数源
            SecureRandom random = new SecureRandom();
            // 创建一个DESKeySpec对象
            DESKeySpec desKey = new DESKeySpec(password.getBytes("UTF-8"));
            // 创建一个密匙工厂
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            // 将DESKeySpec对象转换成SecretKey对象
            SecretKey securekey = keyFactory.generateSecret(desKey);
            // Cipher对象实际完成解密操作
            Cipher cipher = Cipher.getInstance("DES");
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.DECRYPT_MODE, securekey, random);
            // 真正开始解密操作
            return new String(cipher.doFinal(Base64.decodeBase64(src)), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 生成验证码
     */
    public static String genCaptcha(int length) {
        String captcha = "";

        for (int i = 0; i < length; i++) {
            captcha += RandomUtils.nextInt(10);
        }

        return captcha;
    }

    public static Date getEndOfDay(Date expirationAt) {
        Date result = DateUtils.truncate(expirationAt, Calendar.DATE);
        result = DateUtils.addDays(result, 1);
        result = DateUtils.addSeconds(result, -1);

        return result;
    }

    public static <T extends Enum<T>> String getEnumCnName(Class<T> enumType, String name) {
        T instance = null;

        if (StringUtils.isBlank(name)) {
            return "";
        }

        try {
            instance = Enum.valueOf(enumType, name);
        } catch (IllegalArgumentException e) {
            return "";
        }

        if (instance instanceof ICnName) {
            return ((ICnName) instance).getCnName();
        }

        return "";
    }

    public static Date parseEndDate(String endDateStr) {
        if (StringUtils.isEmpty(endDateStr)) {
            return null;
        }

        try {
            Date endDate = DateUtils.parseDateStrictly(endDateStr, new String[]{"yyyy-MM-dd"});
            endDate = DateUtils.addDays(endDate, 1);
            endDate = DateUtils.truncate(endDate, Calendar.DATE);

            return endDate;
        } catch (ParseException e) {
            return null;
        }
    }

    public static Date parseStartDate(String startDateStr) {
        if (StringUtils.isEmpty(startDateStr)) {
            return null;
        }

        try {
            Date startDate = DateUtils.parseDateStrictly(startDateStr, new String[]{"yyyy-MM-dd"});
            startDate = DateUtils.truncate(startDate, Calendar.DATE);

            return startDate;
        } catch (ParseException e) {
            return null;
        }
    }

    /**
     * 四舍五入
     *
     * @param value
     * @return
     */
    public static Double round(Number value) {
        if (value == null) {
            return null;
        }

        return new BigDecimal(value.doubleValue()).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 四舍五入
     *
     * @param value
     * @return
     */
    public static BigDecimal roundToBigDecimal(Number value) {
        if (value == null) {
            return null;
        }

        return new BigDecimal(value.doubleValue()).setScale(2, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 四舍五入
     *
     * @param value
     * @return
     */
    public static Integer roundToInteger(Number value) {
        if (value == null) {
            return null;
        }

        return new BigDecimal(value.doubleValue()).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
    }

    /**
     * 验证集合
     */
    public static void validCollection(Validator validator, Collection collection, Errors errors) {
        for (Object object : collection) {
            ValidationUtils.invokeValidator(validator, object, errors);
        }
    }

    public static GPSCalculateDto gpsCalculate(double lon, double lat, double range) {
        double r = 6371;//地球半径千米range 千米距离
        double dlng = 2 * Math.asin(Math.sin(range / (2 * r)) / Math.cos(lat * Math.PI / 180));
        dlng = dlng * 180 / Math.PI;//角度转为弧度
        double dlat = range / r;
        dlat = dlat * 180 / Math.PI;
        double minlat = lat - dlat;
        double maxlat = lat + dlat;
        double minlng = lon - dlng;
        double maxlng = lon + dlng;
        return new GPSCalculateDto(minlat, maxlat, minlng, maxlng);
    }

    public static double getDistance(double lng1, double lat1, double lng2, double lat2) {
        double EARTH_RADIUS = 6378.137;
        double radLat1 = lat1 * Math.PI / 180.0;
        double radLat2 = lat2 * Math.PI / 180.0;
        double a = radLat1 - radLat2;
        double b = lng1 * Math.PI / 180.0 - lng2 * Math.PI / 180.0;
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000d) / 10000d;
        s = s * 1000;
        return s;
    }

    public static String string2Unicode(String string) {
        StringBuffer unicode = new StringBuffer();
        for (int i = 0; i < string.length(); i++) {
            // 取出每一个字符
            char c = string.charAt(i);
            // 转换为unicode
            unicode.append("\\u" + Integer.toHexString(c));
        }
        return unicode.toString();
    }

    public static String unicode2String(String unicode) {
        StringBuffer string = new StringBuffer();
        String[] hex = unicode.split("\\\\u");
        for (int i = 1; i < hex.length; i++) {
            // 转换出每一个代码点
            int data = Integer.parseInt(hex[i], 16);
            // 追加成string
            string.append((char) data);
        }
        return string.toString();
    }

    public static void main(String[] args) {
        String s = "\\u2728\\u5cb3\\u47\\u41\\u20\\u4b\\u55\\u2728\\u6210\\u7f8e\\u2728\\u2728";
        System.out.println(s);
//        String a = string2Unicode(s);
//        System.out.println(a);

        System.out.println(unicode2String(s));
    }
}
